package com.net.xpay.core.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AccDetailModel;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.*;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.enums.invoice.OpenInvoiceStatus;
import com.net.xpay.common.manager.UserManager;
import com.net.xpay.core.constant.PayMethod;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.enums.*;
import com.net.xpay.core.validator.BuyModelValidator;
import com.net.common.annotation.WriteTransactional;
import com.net.common.exception.BusinessException;
import com.net.common.util.LocalDateUtil;
import com.net.common.util.MoneyUtil;
import com.net.xpay.alipay.AlipayProperties;
import com.net.xpay.alipay.PoseidonAlipayClient;
import com.net.xpay.alipay.common.CommonAlipayClient;
import com.net.xpay.alipay.common.model.AlipayTradeNotifyModel;
import com.net.xpay.alipay.enums.IdentityType;
import com.net.xpay.alipay.model.TransOrderModel;
import com.net.xpay.core.CoreProperties;
import com.net.xpay.core.domain.AccountDetail;
import com.net.xpay.core.domain.AlipayUser;
import com.net.xpay.core.domain.ChargeRecord;
import com.net.xpay.core.domain.UserAccount;
import com.net.xpay.core.enums.*;
import com.net.xpay.core.helper.AccountDetailHelper;
import com.net.xpay.core.helper.QrCodeHelper;
import com.net.xpay.core.manager.AccountDetailManager;
import com.net.xpay.core.manager.AlipayUserManager;
import com.net.xpay.core.manager.ChargeRecordManager;
import com.net.xpay.core.manager.UserAccountManager;
import com.net.xpay.core.model.AdminChargeModel;
import com.net.xpay.core.model.CancelChargeRecordModel;
import com.net.xpay.core.model.ChargeModel;
import com.net.xpay.core.service.AccountService;
import com.net.xpay.core.service.AlipayFundBatchService;
import com.net.xpay.core.service.ChargeService;
import com.net.xpay.core.utils.GsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 2020/5/12 14:42

 */
@Slf4j
@Service
public class ChargeServiceImpl implements ChargeService {

    @Autowired
    private BuyModelValidator buyModelValidator;

    @Autowired
    private PoseidonAlipayClient xpayAlipayClient;

    @Autowired
    private AlipayUserManager alipayUserManager;

    @Autowired
    private AlipayProperties alipayProperties;

    @Autowired
    private ChargeRecordManager chargeRecordManager;

    @Autowired
    private UserManager userManager;

    @Autowired
    private AlipayFundBatchService alipayFundBatchService;

    @Autowired
    private UserAccountManager userAccountManager;

    @Autowired
    private AccountDetailHelper accountDetailHelper;

    @Autowired
    private AccountService accountService;

    @Autowired
    private AccountDetailManager accountDetailManager;

    @Autowired
    private CoreProperties coreProperties;

    @Autowired
    private QrCodeHelper qrCodeHelper;

    @Autowired
    private CommonAlipayClient commonAlipayClient;


    @Override
    @WriteTransactional
    public ChargeRecord doCharge(User user, ChargeModel chargeModel) {
        buyModelValidator.ensurePayParameter(user, chargeModel);
        if (chargeModel.getBillPayMethod() == BillPayMethod.ALIPAY_APP_QRCODE) {
            //普通扫描付款
            return doChargeByAlipayAppQrcode(user, chargeModel);
        } else if (chargeModel.getBillPayMethod() == BillPayMethod.ALIPAY_WEB_PAGE) {
            //电脑网站支付
            return doChargeByAlipayWebPage(user, chargeModel);
        } else {
            //签约付款
            return doChargeBySignPay(user, chargeModel);
        }
    }

    private ChargeRecord doChargeByAlipayWebPage(User user, ChargeModel chargeModel) {
        ChargeRecord chargeRecord = ChargeRecord.getInstance(user);
        chargeRecord.setAmount(chargeModel.getAmount());
        chargeRecord.setBillPayMethod(chargeModel.getBillPayMethod());
        chargeRecord.setIdentity(coreProperties.getAlipayReceiveAccount());
        chargeRecord.setName(coreProperties.getAlipayReceiveName());
        chargeRecord.setIdentityType(IdentityType.ALIPAY_LOGON_ID.getValue());
        chargeRecord.setPayStatus(PayStatus.WAIT_PAY);
        chargeRecord.setOpenInvoiceStatus(OpenInvoiceStatus.WAIT);
        chargeRecord.setNotifyUrl(chargeModel.getNotifyUrl());

        long expireMinutes = chargeModel.getExpireMinutes() == null ? 30L : chargeModel.getExpireMinutes();
        chargeRecord.setTimeExpire(LocalDateTime.now().plusMinutes(expireMinutes));
        chargeRecord.setImportBizNo(chargeModel.getImportBizNo());
        chargeRecordManager.save(chargeRecord);


        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();

        request.setReturnUrl(Strings.isNullOrEmpty(chargeModel.getReturnUrl()) ? coreProperties.getCommonPayReturnUrl() : chargeModel.getReturnUrl());
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", chargeRecord.getOutBizNo());
        bizContent.put("total_amount", MoneyUtil.convertCentToString(chargeRecord.getAmount(), 2));
        bizContent.put("subject", "充值服务费" + MoneyUtil.convertCentToString(chargeRecord.getAmount(), 2) + "元");
        //电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAY
        bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");

        bizContent.put("time_expire", LocalDateUtil.getDateTimeAsString(chargeRecord.getTimeExpire()));

        // 商品明细信息，按需传入
        JSONArray goodsDetail = new JSONArray();
        JSONObject goods1 = new JSONObject();
        goods1.put("goods_id", chargeRecord.getOutBizNo());
        goods1.put("goods_name", "充值服务费" + MoneyUtil.convertCentToString(chargeRecord.getAmount(), 2) + "元");
        goods1.put("quantity", 1);
        goods1.put("price", MoneyUtil.convertCentToString(chargeRecord.getAmount(), 2));
        goodsDetail.add(goods1);
        bizContent.put("goods_detail", goodsDetail);

        request.setBizContent(bizContent.toJSONString());

        request.setNotifyUrl(coreProperties.getCommonPayNotifyUrl());

        try {
            AlipayTradePagePayResponse resp = commonAlipayClient.createAlipayTradePagePay(request);
            if (!resp.isSuccess()) {
                log.warn("doChargeByAlipayWebPage warn|userId={}|errorCode={}|errorMsg={}|bizContent={}", user.getId(), resp.getSubCode(), resp.getSubMsg(), request.getBizContent());
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "发起充值失败，系统繁忙：" + resp.getSubCode());
            }

            chargeRecord.setLastPayUrl(resp.getBody());
            chargeRecordManager.update(chargeRecord);
            return chargeRecord;
        } catch (Exception e) {
            log.error("doChargeByAlipayWebPage error|userId={}|bizContent={}", user.getId(), request.getBizContent(), e);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统繁忙");
        }
    }

    private ChargeRecord doChargeByAlipayAppQrcode(User user, ChargeModel chargeModel) {
        ChargeRecord chargeRecord = ChargeRecord.getInstance(user);
        chargeRecord.setAmount(chargeModel.getAmount());
        chargeRecord.setBillPayMethod(chargeModel.getBillPayMethod());
        chargeRecord.setIdentity(coreProperties.getAlipayReceiveAccount());
        chargeRecord.setName(coreProperties.getAlipayReceiveName());
        chargeRecord.setIdentityType(IdentityType.ALIPAY_LOGON_ID.getValue());
        chargeRecord.setPayStatus(PayStatus.WAIT_PAY);
        chargeRecord.setOpenInvoiceStatus(OpenInvoiceStatus.WAIT);
        chargeRecord.setNotifyUrl(chargeModel.getNotifyUrl());

        long expireMinutes = chargeModel.getExpireMinutes() == null ? 30L : chargeModel.getExpireMinutes();
        chargeRecord.setTimeExpire(LocalDateTime.now().plusMinutes(expireMinutes));
        chargeRecord.setImportBizNo(chargeModel.getImportBizNo());
        chargeRecordManager.save(chargeRecord);


        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();

        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", chargeRecord.getOutBizNo());
        bizContent.put("total_amount", MoneyUtil.convertCentToString(chargeRecord.getAmount(), 2));
        bizContent.put("subject", "服务费充值");
        bizContent.put("time_expire", LocalDateUtil.getDateTimeAsString(chargeRecord.getTimeExpire()));

        // 商品明细信息，按需传入
        JSONArray goodsDetail = new JSONArray();
        JSONObject goods1 = new JSONObject();
        goods1.put("goods_id", chargeRecord.getOutBizNo());
        goods1.put("goods_name", "服务费充值");
        goods1.put("quantity", 1);
        goods1.put("price", MoneyUtil.convertCentToString(chargeRecord.getAmount(), 2));
        goodsDetail.add(goods1);
        bizContent.put("goods_detail", goodsDetail);

        request.setBizContent(bizContent.toJSONString());

        request.setNotifyUrl(coreProperties.getCommonPayNotifyUrl());

        try {
            AlipayTradePrecreateResponse resp = commonAlipayClient.preCreateAlipayTrade(request);
            if (!resp.isSuccess()) {
                log.warn("doChargeByAlipayAppQrcode warn|userId={}|errorCode={}|errorMsg={}|bizContent={}", user.getId(), resp.getSubCode(), resp.getSubMsg(), request.getBizContent());
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统繁忙：" + resp.getSubCode());
            }
            String qrCode = qrCodeHelper.genThenUpload(resp.getQrCode());
            chargeRecord.setMobilePayQrCode(qrCode);
            chargeRecordManager.update(chargeRecord);
            return chargeRecord;
        } catch (Exception e) {
            log.error("doChargeByAlipayAppQrcode error|userId={}|bizContent={}", user.getId(), request.getBizContent(), e);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统繁忙");
        }

    }

    private ChargeRecord doChargeBySignPay(User user, ChargeModel chargeModel) {
        if (user.getAlipayUserId() == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "你尚未绑定支付宝账号");
        }
        AlipayUser alipayUser = alipayUserManager.getById(user.getAlipayUserId());
        if (alipayUser == null || alipayUser.getStatus() != AlipayUserStatus.YES) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "你的支付宝账号尚未签约，请先签约后再打款");
        }

        ChargeRecord chargeRecord = ChargeRecord.getInstance(user);
        chargeRecord.setAlipayUserId(alipayUser.getId());
        chargeRecord.setAlipayNickName(alipayUser.getAlipayNickName());
        chargeRecord.setAmount(chargeModel.getAmount());
        chargeRecord.setBillPayMethod(chargeModel.getBillPayMethod());
        chargeRecord.setIdentity(coreProperties.getAlipayReceiveAccount());
        chargeRecord.setName(coreProperties.getAlipayReceiveName());
        chargeRecord.setIdentityType(IdentityType.ALIPAY_LOGON_ID.getValue());
        chargeRecord.setPayStatus(PayStatus.WAIT_PAY);
        long expireMinutes = chargeModel.getExpireMinutes() == null ? 30L : chargeModel.getExpireMinutes();
        chargeRecord.setTimeExpire(LocalDateTime.now().plusMinutes(expireMinutes));
        chargeRecordManager.save(chargeRecord);

        alipayFundBatchCreate(chargeRecord, alipayUser);

        //发起支付链接
        String returnUrl = alipayProperties.getChargeReturnUrl() + "?chargeId=" + chargeRecord.getId();
        try {
            if (chargeModel.getPayMethod() == null || PayMethod.PC.equals(chargeModel.getPayMethod())) {
                String payUrl = xpayAlipayClient.alipayFundTransPagePay(alipayUser.getAuthToken(), chargeRecord.getBatchTransId(), returnUrl);
                chargeRecord.setLastPayUrl(payUrl);
            } else {
                createMobilePayUrl(chargeRecord, alipayUser, returnUrl);
            }

            chargeRecord.setPayStatus(PayStatus.WAIT_PAY);
            chargeRecordManager.update(chargeRecord);
            return chargeRecord;
        } catch (Exception e) {
            log.warn("doCharge error|userId={}", user.getId(), e);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统异常");
        }
    }

    private void createMobilePayUrl(ChargeRecord chargeRecord, AlipayUser alipayUser, String returnUrl) throws IOException {
        String mobilePayUrl = xpayAlipayClient.alipayFundBatchAppPay(alipayUser.getAuthToken(), chargeRecord.getBatchTransId(), returnUrl);

        // 初始支付参数
        String param = mobilePayUrl.substring(mobilePayUrl.indexOf("?") + 1);
        // 将支付参数整体 encode 一次
        String encodeParam = URLEncoder.encode(param, "utf-8");
        // 拼接带参页面路径并再次整体encode
        String pageEncode = URLEncoder.encode("pages/confirm/index/index?signParams=" + encodeParam, "utf-8");
        // 拼接跳转支付宝内支付地址
        mobilePayUrl = "alipays://platformapi/startapp?appId=2021002133661287&appClearTop=false&startMultApp=YES"
                + "&page=" + pageEncode;

        // 拼接完整跳转链接
        mobilePayUrl = "https://render.alipay.com/p/s/ulink/?scheme=" + URLEncoder.encode(mobilePayUrl, "utf-8");
        chargeRecord.setLastMobilePayUrl(mobilePayUrl);

        //二维码链接地址
        String qrCode = qrCodeHelper.genThenUpload(alipayProperties.getMobileChargeBaseUrl() + chargeRecord.getOutBizNo());
        chargeRecord.setMobilePayQrCode(qrCode);
    }

    @Override
    @WriteTransactional
    public void updateCharge(ChargeRecord chargeRecord) {
        chargeRecord = chargeRecordManager.getById(chargeRecord.getId());
        User user = userManager.getById(chargeRecord.getUserId());
        if (chargeRecord.getPayStatus() != PayStatus.WAIT_PAY) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "充值状态不是待付款");
        }

        if (chargeRecord.getBillPayMethod() == BillPayMethod.ALIPAY_APP_QRCODE || chargeRecord.getBillPayMethod() == BillPayMethod.ALIPAY_WEB_PAGE) {
            //二维码当面付扫码或者电脑网站支付的
            doUpdateCommonCharge(user, chargeRecord);
        } else {
            //签约充值的
            doUpdateSignCharge(user, chargeRecord);
        }
    }

    //只处理超时关闭的，成功的由推送处理
    private void doUpdateCommonCharge(User user, ChargeRecord chargeRecord) {
        try {
            AlipayTradeQueryResponse resp = commonAlipayClient.queryAlipayTrade(chargeRecord.getOutBizNo());
            if ("ACQ.TRADE_NOT_EXIST".equals(resp.getSubCode())) {
                //交易不存在说明没有付款
                if (chargeRecord.getTimeExpire() != null && chargeRecord.getTimeExpire().isBefore(LocalDateTime.now())) {
                    chargeRecord.setErrorMsg("超时未付款关闭");
                    chargeRecord.setPayStatus(PayStatus.CANCELLED);
                    chargeRecord.setEndTime(LocalDateTime.now());
                    chargeRecordManager.update(chargeRecord);
                    return;
                }
                if (chargeRecord.getCreateTime().isBefore(LocalDateTime.now().minusMinutes(60L))) {
                    chargeRecord.setErrorMsg("超时未付款关闭");
                    chargeRecord.setPayStatus(PayStatus.CANCELLED);
                    chargeRecord.setEndTime(LocalDateTime.now());
                    chargeRecordManager.update(chargeRecord);
                    return;
                }
            }
            if ("TRADE_CLOSED".equals(resp.getTradeStatus())) {
                chargeRecord.setErrorMsg("超时未付款关闭");
                chargeRecord.setPayStatus(PayStatus.CANCELLED);
                chargeRecord.setEndTime(LocalDateTime.now());
                chargeRecordManager.update(chargeRecord);
            }
            if ("TRADE_SUCCESS".equals(resp.getTradeStatus())) {
                //10分钟了推送还没处理
                if (chargeRecord.getCreateTime().isBefore(LocalDateTime.now().minusMinutes(10L))) {
                    AccountDetail accountDetail = accountDetailManager.findByRelatedId(chargeRecord.getUserId(), chargeRecord.getId(), AccountBillType.CHARGE);
                    if (accountDetail != null) {
                        log.warn("updateCharge warn detail exist|id={}", chargeRecord.getId());
                        return;
                    }

                    //跟新订单状态
                    chargeRecord.setPayStatus(PayStatus.PAYED);
                    chargeRecord.setDetailId(resp.getTradeNo());
                    chargeRecord.setEndTime(LocalDateUtil.fromDate(resp.getSendPayDate()));
                    chargeRecordManager.update(chargeRecord);

                    onChargeSuccess(chargeRecord);
                    return;
                }

            }
        } catch (AlipayApiException e) {
            log.error("doUpdateCommonCharge error", e);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "查询异常");
        }
    }

    private void doUpdateSignCharge(User user, ChargeRecord chargeRecord) {
        if (chargeRecord.getCreateTime().isBefore(LocalDateTime.now().minusDays(1L))) {
            chargeRecord.setErrorMsg("超时未付款关闭");
            chargeRecord.setPayStatus(PayStatus.CANCELLED);
            chargeRecord.setEndTime(LocalDateTime.now());
            chargeRecordManager.update(chargeRecord);
            return;
        }

        if (chargeRecord.getBatchTransId() == null) {
            log.warn("updateCharge batchTransId null|userId={}|id={}", user.getId(), chargeRecord.getId());
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "充值尚未发起支付");
        }

        AccountDetail accountDetail = accountDetailManager.findByRelatedId(chargeRecord.getUserId(), chargeRecord.getId(), AccountBillType.CHARGE);
        if (accountDetail != null) {
            log.warn("updateCharge warn detail exist|userId={}|id={}", user.getId(), chargeRecord.getId());
            return;
        }
        try {
            AlipayFundBatchDetailQueryResponse resp = alipayFundBatchService.allDetailQuery(chargeRecord.getAlipayUserId(), chargeRecord.getOutBizNo());
            String batchStatus = resp.getBatchStatus();
            // INIT 接受 | WAIT_PAY 等待付款 不处理
            if (PayTransBatchStatus.INIT.getValue().equals(batchStatus) || PayTransBatchStatus.WAIT_PAY.getValue().equals(batchStatus) || PayTransBatchStatus.DEALING.getValue().equals(batchStatus)) {
                log.info("updateCharge resp batch status = {}|userId={}|id={}", batchStatus, user.getId(), chargeRecord.getId());
                return;
            }

            respProcess(chargeRecord, resp);
        } catch (AlipayApiException e) {
            log.error("updateCharge error|userId={}|id={}", user.getId(), chargeRecord.getId());
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "查询失败");
        }
    }

    @Override
    @WriteTransactional
    public ChargeRecord doChargeByAdmin(AdminChargeModel chargeModel) {
        Preconditions.checkArgument(chargeModel.getUserId() != null && chargeModel.getAmount() != null);
        if (chargeModel.getAmount() <= 0L) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "充值金额必须大于0");
        }
        User merchant = userManager.getById(chargeModel.getUserId());

        ChargeRecord chargeRecord = ChargeRecord.getInstance();
        chargeRecord.setUserId(chargeModel.getUserId());
        chargeRecord.setPartnerId(merchant.getPartnerId());
        chargeRecord.setFatherPartnerId(merchant.getFatherPartnerId());
        chargeRecord.setAmount(chargeModel.getAmount());
        chargeRecord.setPayStatus(PayStatus.PAYED);
        chargeRecord.setBillPayMethod(BillPayMethod.BY_ADMIN);
        chargeRecord.setIdentity(coreProperties.getAlipayReceiveAccount());
        chargeRecord.setName(coreProperties.getAlipayReceiveName());
        chargeRecord.setIdentityType(IdentityType.ALIPAY_LOGON_ID.getValue());
        chargeRecord.setEndTime(LocalDateTime.now());
        chargeRecord.setMemo(chargeModel.getMemo());

        chargeRecordManager.save(chargeRecord);

        onChargeSuccess(chargeRecord);
        return chargeRecord;
    }

    @Override
    @WriteTransactional
    public void doChargeTransBatchByAdmin(AdminChargeModel chargeModel) {
        Preconditions.checkArgument(chargeModel.getUserId() != null && chargeModel.getAmount() != null);
        if (chargeModel.getAmount() == 0L) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "充值次数不能为0");
        }
        UserAccount userAccount = userAccountManager.getByIdWithLock(chargeModel.getUserId());
        User user = userManager.getById(chargeModel.getUserId());
        AccountDetail accountDetail = AccountDetail.getInstance(chargeModel.getUserId(), System.currentTimeMillis());
        accountDetailHelper.initTransBatchAccountDetail(accountDetail, AccountBillType.TRANS_BATCH, chargeModel.getAmount(), "充值体验次数");
        accountService.onPay(user, userAccount, accountDetail);
    }

    @Override
    @WriteTransactional
    public ChargeRecord onCommonPayNotify(AlipayTradeNotifyModel notifyModel) {
        log.info("onCommonPayNotify notifyModel={}", JSONObject.toJSONString(notifyModel));

        if (!Strings.isNullOrEmpty(notifyModel.getRefundFee())) {
            return null;
        }

        if ("TRADE_CLOSED".equals(notifyModel.getTradeStatus())) {
            //订单已关闭
            ChargeRecord chargeRecord = chargeRecordManager.getByOutBizNo(notifyModel.getOutTradeNo());
            chargeRecord.setPayStatus(PayStatus.CANCELLED);
            chargeRecord.setEndTime(LocalDateTime.now());
            chargeRecordManager.update(chargeRecord);
            return chargeRecord;
        }

        if (!Objects.equals(notifyModel.getTradeStatus(), "TRADE_SUCCESS")) {
            return null;
        }

        //查找订单
        ChargeRecord chargeRecord = chargeRecordManager.getByOutBizNo(notifyModel.getOutTradeNo());
        if (chargeRecord == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "订单不存在");
        }
        if (chargeRecord.getPayStatus() != PayStatus.WAIT_PAY) {
            log.info("onCommonPayNotify chargeRecord status is not wait_pay|id={}", chargeRecord.getId());
            return null;
        }
        if (new BigDecimal(notifyModel.getTotalAmount()).compareTo(MoneyUtil.convertCentToBigDecimal(chargeRecord.getAmount(), 2)) != 0) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付价格不匹配");
        }

        AccountDetail accountDetail = accountDetailManager.findByRelatedId(chargeRecord.getUserId(), chargeRecord.getId(), AccountBillType.CHARGE);
        if (accountDetail != null) {
            log.warn("updateCharge warn detail exist|id={}", chargeRecord.getId());
            return null;
        }

        //跟新订单状态
        chargeRecord.setPayStatus(PayStatus.PAYED);
        chargeRecord.setDetailId(notifyModel.getTradeNo());
        chargeRecord.setEndTime(LocalDateUtil.parseStringToDateTime(notifyModel.getGmtPayment()));
        chargeRecordManager.update(chargeRecord);

        onChargeSuccess(chargeRecord);

        return chargeRecord;
    }

    @Override
    @WriteTransactional
    public void cancelChargeRecord(CancelChargeRecordModel model) {
        Preconditions.checkArgument(model.getUserId() != null && model.getId() != null);
        ChargeRecord chargeRecord = chargeRecordManager.getById(model.getId());
        if (chargeRecord == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "订单不存在");
        }
        if (!PayStatus.PAYED.equals(chargeRecord.getPayStatus())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "充值订单状态错误");
        }
        if (chargeRecord.getBillPayMethod() != BillPayMethod.BY_ADMIN) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "只有后台充值的订单可以作废");
        }
        if (chargeRecord.getEndTime().isBefore(LocalDateTime.now().minusDays(2L))) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "充值时间超过48小时，不可操作");
        }
        //更新金额
        UserAccount userAccount = userAccountManager.getByIdWithLock(chargeRecord.getUserId());
        User user = userManager.getById(chargeRecord.getUserId());
        AccountDetail accountDetail = AccountDetail.getInstance(chargeRecord.getUserId(), chargeRecord.getId());
        accountDetailHelper.initCommonAccountDetail(accountDetail, AccountBillType.CANCEL_CHARGE, -chargeRecord.getAmount(), "作废充值金额");
        accountService.onPay(user, userAccount, accountDetail);
        //更新充值记录状态
        chargeRecord.setPayStatus(PayStatus.CANCELLED);
        chargeRecordManager.update(chargeRecord);

    }


    private void respProcess(ChargeRecord chargeRecord, AlipayFundBatchDetailQueryResponse response) {
        if (!response.isSuccess()) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "查询失败:" + response.getSubMsg() + "|" + response.getSubCode());
        }

        AccDetailModel accDetail = response.getAccDetailList().get(0);

        if (Objects.equals(accDetail.getStatus(), PayTransStatus.SUCCESS.getValue())) {
            chargeRecord.setPayStatus(PayStatus.PAYED);
            onChargeSuccess(chargeRecord);
        } else {
            chargeRecord.setErrorMsg(accDetail.getErrorMsg());
            chargeRecord.setErrorCode(accDetail.getErrorCode());
            chargeRecord.setPayStatus(PayStatus.CANCELLED);
        }
        chargeRecord.setDetailId(accDetail.getDetailId());
        chargeRecord.setEndTime(LocalDateTime.now());
        chargeRecordManager.update(chargeRecord);
    }

    private void onChargeSuccess(ChargeRecord chargeRecord) {
        UserAccount userAccount = userAccountManager.getByIdWithLock(chargeRecord.getUserId());
        User user = userManager.getById(chargeRecord.getUserId());
        AccountDetail accountDetail = AccountDetail.getInstance(chargeRecord.getUserId(), chargeRecord.getId());
        accountDetailHelper.initCommonAccountDetail(accountDetail, AccountBillType.CHARGE, chargeRecord.getAmount(), "");
        accountService.onPay(user, userAccount, accountDetail);

        user = userManager.getById(chargeRecord.getUserId());
        user.setLastChargeTime(chargeRecord.getEndTime());
        if (user.getChargeTime() == null) {
            user.setChargeTime(chargeRecord.getEndTime());
        }
        userManager.updateByPrimaryKeyWithVersion(user);
    }


    private void alipayFundBatchCreate(ChargeRecord chargeRecord, AlipayUser alipayUser) {
        TransOrderModel transOrderModel = buildTransOrderModel(chargeRecord);
        log.info("doCharge alipayFundBatchCreate params userId={}|transOrderModel={}", alipayUser.getId(), GsonUtil.getGson().toJson(transOrderModel));
        AlipayFundBatchCreateResponse response;
        try {
            response = xpayAlipayClient.alipayFundBatchCreate(alipayUser.getAuthToken(), transOrderModel);
        } catch (AlipayApiException e) {
            log.warn("doCharge alipayFundBatchCreate error|id={}", chargeRecord.getId(), e);
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "充值失败：" + e.getErrMsg());
        }
        if (!response.isSuccess()) {
            log.warn("doCharge alipayFundBatchCreate failed userId={}|errorMsg={}", alipayUser.getId(), response.getSubMsg() + "|" + response.getMsg());
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "充值失败：" + response.getSubMsg() + "|" + response.getMsg());
        }

        chargeRecord.setBatchTransId(response.getBatchTransId());
    }

    private TransOrderModel buildTransOrderModel(ChargeRecord chargeRecord) {
        TransOrderModel transOrderModel = TransOrderModel.getInstance();
        transOrderModel.setOutBatchNo(chargeRecord.getOutBizNo());
        transOrderModel.setTotalTransAmount(MoneyUtil.convertCentToString(chargeRecord.getAmount(), 2));
        transOrderModel.setTotalCount("1");
        transOrderModel.setProductCode("BATCH_API_TO_ACC");
        transOrderModel.setBizScene("MESSAGE_BATCH_PAY");
        transOrderModel.setOrderTitle("充值");
        transOrderModel.setTimeExpire(LocalDateUtil.getDateTimeAsString(chargeRecord.getTimeExpire(), "yyyy-MM-dd HH:mm"));
        transOrderModel.setRemark(null);
        List<TransOrderModel.TransOrderDetail> transOrderDetails = Lists.newArrayList();

        TransOrderModel.TransOrderDetail detail = TransOrderModel.TransOrderDetail.getInstance();
        detail.setOutBizNo(chargeRecord.getOutBizNo());
        detail.setTransAmount(MoneyUtil.convertCentToString(chargeRecord.getAmount(), 2));
        detail.setRemark(null);

        TransOrderModel.PayeeInfo payeeInfo = TransOrderModel.PayeeInfo.getInstance();
        payeeInfo.setIdentity(chargeRecord.getIdentity());
        payeeInfo.setIdentityType(chargeRecord.getIdentityType());
        payeeInfo.setName(chargeRecord.getName());

        detail.setPayeeInfo(payeeInfo);

        transOrderDetails.add(detail);

        transOrderModel.setTransOrderList(transOrderDetails);
        return transOrderModel;
    }
}
